/*
 **	Database.cpp
 **
 **	Published / author: 2006-03-29 / grymse@alhem.net
 **/
/*
Copyright (C) 2006 Anders Hedstrom

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#ifdef _WIN32
#pragma warning(disable:4786)
#endif

#ifdef _WIN32
#include <afxdb.h>
#include <sql.h>
#endif

#include "Database.h"
#include "Query.h"
#include "IError.h"
#include "Session.h"


#ifdef ODBCW_NAMESPACE
namespace ODBCW_NAMESPACE {
#endif


Database::Database(const std::string& dsn,const std::string& u,const std::string& auth,IError *p)
:m_driverconnect(0)
,m_host(dsn)
,m_user(u)
,m_password(auth)
,m_errc(0)
,m_escape('\'')
,m_decimal(',')
,m_errhandler(p)
,m_mutex(m_mutex)
,m_b_use_mutex(false)
{
}


Database::Database(const std::string& driverconnect,IError *p)
:m_driverconnect(1)
,m_host(driverconnect)
,m_errc(0)
,m_escape('\'')
,m_decimal(',')
,m_errhandler(p)
,m_mutex(m_mutex)
,m_b_use_mutex(false)
{
}


Database::Database(Mutex& mutex,const std::string& dsn,const std::string& u,const std::string& auth,IError *p)
:m_driverconnect(0)
,m_host(dsn)
,m_user(u)
,m_password(auth)
,m_errc(0)
,m_escape('\'')
,m_decimal(',')
,m_errhandler(p)
,m_mutex(mutex)
,m_b_use_mutex(true)
{
}


Database::Database(Mutex& mutex,const std::string& driverconnect,IError *p)
:m_driverconnect(1)
,m_host(driverconnect)
,m_errc(0)
,m_escape('\'')
,m_decimal(',')
,m_errhandler(p)
,m_mutex(mutex)
,m_b_use_mutex(true)
{
}


Database::~Database()
{
	for (std::list<Session *>::iterator it = m_sessions.begin(); it != m_sessions.end(); it++)
	{
		if ((*it) -> IsBusy())
		{
			error("destroying Session object before Query object(s)\n");
		}
		Session *pSession = static_cast<Session*>(*it);
		delete pSession;
	}
}


Session *Database::grabdb()
{
	Lock lck(m_mutex, m_b_use_mutex);
	for (std::list<Session *>::iterator it = m_sessions.begin(); it != m_sessions.end(); it++)
	{
		if (!(*it) -> IsBusy())
		{
			(*it) -> SetBusy(true);
			return *it;
		}
	}
	Session *tmp = new Session;
	int nResult;
	if (m_driverconnect)
	{
		nResult = tmp -> Connect(*this, m_host);
	}
	else
	{
		nResult = tmp -> Connect(*this, m_host,m_user,m_password);
	}
	if (nResult != 0)
	{
		std::string strSqlState;
		std::string strErrorMessage;
		tmp -> GetLastError(strSqlState, strErrorMessage);
		delete tmp;
		throw strErrorMessage;
	}
	m_sessions.insert(m_sessions.end(), tmp);
	tmp -> SetBusy(true);
	return tmp;
}

void Database::freedb(Session *odb)
{
	Lock lck(m_mutex, m_b_use_mutex);
	odb -> SetBusy(false);
}


short Database::errcode()
{
	return m_errc;
}


void Database::GetErrorText(char *dest)
{
	short rc = m_errc;

	switch (rc)
	{
	case -1:
		sprintf(dest, "%d: SQLAllocHandle(SQL_HANDLE_ENV) failed", rc);
		break;
	case -2:
		sprintf(dest, "%d: SQLSetEnvAttr() failed", rc);
		break;
	case -3:
		sprintf(dest, "%d: SQLAllocHandle(SQL_HANDLE_DBC) failed", rc);
		break;
	case -4:
		sprintf(dest, "%d: SQLConnect(%s) failed", rc, m_host.c_str());
		break;

	default:
		sprintf(dest, "%d: Undefined error code", rc);
	}
}


Query *Database::GetQuery()
{
	return new Query(*this);
}


Query *Database::GetQuery(const std::string& sql)
{
	return new Query(*this, sql);
}


void Database::SetEscapeChar(char ch)
{
	m_escape = ch;
}


char Database::GetEscapeChar()
{
	return m_escape;
}


void Database::SetDecimalChar(char ch)
{
	m_decimal = ch;
}


char Database::GetDecimalChar()
{
	return m_decimal;
}


void Database::error(const std::string& text)
{
	if (m_errhandler)
	{
		m_errhandler -> error(*this, text);
	}
}


Database::Lock::Lock(Mutex& mutex,bool use) : m_mutex(mutex),m_b_use(use)
{
	if (m_b_use)
	{
		m_mutex.Lock();
	}
}


Database::Lock::~Lock()
{
	if (m_b_use)
	{
		m_mutex.Unlock();
	}
}


Database::Mutex::Mutex()
{
#ifdef _WIN32
	m_mutex = ::CreateMutex(NULL, FALSE, NULL);
#else
	pthread_mutex_init(&m_mutex, NULL);
#endif
}


Database::Mutex::~Mutex()
{
#ifdef _WIN32
	::CloseHandle(m_mutex);
#else
	pthread_mutex_destroy(&m_mutex);
#endif
}


void Database::Mutex::Lock()
{
#ifdef _WIN32
	DWORD d = WaitForSingleObject(m_mutex, INFINITE);
	// %! check 'd' for result
#else
	pthread_mutex_lock(&m_mutex);
#endif
}


void Database::Mutex::Unlock()
{
#ifdef _WIN32
	::ReleaseMutex(m_mutex);
#else
	pthread_mutex_unlock(&m_mutex);
#endif
}


std::string Database::safestr(const std::string& s)
{
	std::string s2;
	for (size_t i = 0; i < s.size(); i++)
	{
		switch (s[i])
		{
		case '\'':
		case '\\':
		case 34:
			s2 += GetEscapeChar();
		default:
			s2 += s[i];
		}
	}
	return s2;
}


std::string Database::xmlsafestr(const std::string& str)
{
	std::string str2;

	for (size_t i = 0; i < str.size(); i++)
	{
		switch (str[i])
		{
		case '&':
			str2 += "&amp;";
			break;
		case '<':
			str2 += "&lt;";
			break;
		case '>':
			str2 += "&gt;";
			break;
		case '"':
			str2 += "&quot;";
			break;
		case '\'':
			str2 += "&apos;";
			break;
		default:
			str2 += str[i];
		}
	}
	return str2;
}


#ifdef ODBCW_NAMESPACE
} // namespace ODBCW_NAMESPACE {
#endif
